<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <link rel="stylesheet" type="text/css" href="../css/jquery.datetimepicker.min.css" />
    <script src="../js/jquery.js" type="application/javascript"></script>
    <script src="../js/jquery.datetimepicker.full.min.js" type="application/javascript"></script>
    <!-- <script src="../js/jquery.datetimepicker.min.js" type="application/javascript"></script> -->
    <script src="../js/jquery.mousewheel.min.js" type="application/javascript"></script>
    <script src="../js/plotly-locale-zh-cn-latest.js" type="application/javascript"></script>
    <script src="../js/plotly-latest.min.js" type="application/javascript"></script>
    <style type="text/css">
        @font-face {
            font-family: NotoSansSC;
            src: url('../fonts/NotoSansSC-Regular-Slim.otf') format('opentype');
        }

        .busyness {
            cursor: progress !important;
        }

        .grabbing {
            cursor: grabbing !important;
        }

        .hide {
            display: none !important;
        }

        #timeaxis_bar {
            position: absolute;
            border: 1px solid #97B;
            border-radius: 5px;
            width: 100%;
            height: 30px;
            margin-top: 10px;
            cursor: crosshair;
            overflow: hidden;
        }

        #timeaxis_single_mark {
            display: none;
            position: absolute;
            border-radius: 5px;
            width: 2px;
            height: 32px;
            margin-left: 120px;
            margin-top: 10px;
            background-color: #DDDD;
            z-index: 0;
        }

        #timeaxis_slider {
            display: none;
            position: absolute;
            border: 2px solid #B97;
            border-radius: 5px;
            width: 100px;
            height: 48px;
            margin-left: 100px;
            background-color: #B977;
            cursor: grab;
            z-index: 1;
        }

        .timeaxis_slider_border {
            position: absolute;
            border: 2px solid #B97;
            border-radius: 5px;
            width: 8px;
            height: 20px;
            margin-top: 12px;
            background-color: #FFF;
            z-index: 2;
            cursor: col-resize;
        }

        #timeaxis_hoverbar {
            position: absolute;
            width: 10px;
            height: 100%;
            border-left: 1px solid #777;
            border-right: 1px solid #777;
            background-color: #7777;
            display: none;
            z-index: 3;
        }

        #timeaxis_future {
            position: absolute;
            right: 0px;
            height: 30px;
            background: linear-gradient(-45deg,
                    #7777 0, #7777 25%, transparent 25%, transparent 50%,
                    #7777 50%, #7777 75%, transparent 75%, transparent);
            background-size: 10px 10px;
            display: none;
        }

        .show {
            display: block !important;
        }

        .timeaxis_btn {
            position: relative;
            display: inline-block;
            width: 24px;
            height: 24px;
            font-size: 14px;
            z-index: 5;
            padding: 1px;
            cursor: pointer !important;
            margin-top: 14px;
            margin-left: 6px;
        }

        #timeaxis_reset {
            display: none;
            left: 50%;
            margin-left: -12px;
        }

        #timeaxis_tooltip {
            position: absolute;
            display: none;
            border: 1px solid #333;
            background-color: #FFF;
            padding: 4px 10px 4px 10px;
            white-space: nowrap;
            top: 50px;
            z-index: 4;
        }

        #timeaxis:hover #timeaxis_tooltip {
            display: block !important;
        }

        #timeaxis_label>span {
            margin-top: 3px;
            height: 18px;
            position: absolute;
            width: 160px;
            text-align: center;
            vertical-align: middle;
            line-height: 18px;
        }

        #extendtext_parent {
            position: absolute;
            height: 25px;
            width: 100%;
            top: 55px;
            z-index: 9999;
        }

        #extendtext {
            display: none;
            color: dimgrey;
            text-align: center;
            margin-right: 260px;
        }

        #next_view_menu span {
            display: block;
            padding: 5px 5px 5px 5px;
            cursor: pointer;
        }

        #next_view_menu span:hover {
            color: blue;
        }

        @keyframes spin {
            from {
                transform: rotate(0deg);
            }

            to {
                transform: rotate(360deg);
            }
        }

        .cross {
            position: relative;
            width: 20px !important;
            height: 20px !important;
        }

        .cross::before {
            content: '';
            position: absolute;
            top: 0px;
            left: 10px;
            width: 1px;
            height: 20px;
            background-color: black;
        }

        .cross::after {
            content: '';
            position: absolute;
            top: 10px;
            left: 0px;
            width: 20px;
            height: 1px;
            background-color: black;
        }

        .line {
            position: relative;
            width: 100%;
            height: 1px;
            background-color: black;
        }

        .line::before {
            content: '';
            position: absolute;
            top: -5px;
            left: 0px;
            width: 1px;
            height: 11px;
            background-color: black;
        }

        .line::after {
            content: '';
            position: absolute;
            top: -5px;
            right: 0px;
            width: 1px;
            height: 11px;
            background-color: black;
        }
    </style>
    <script type="text/javascript">
        Date.prototype.format = function (format) {
            return format
                .replace('yyyy', this.getFullYear())
                .replace('yy', `0${this.getFullYear()}`.slice(-2))
                .replace('MM', `0${this.getMonth() + 1}`.slice(-2))
                .replace('M', (this.getMonth() + 1))
                .replace('dd', `0${this.getDate()}`.slice(-2))
                .replace('d', (this.getDate()))
                .replace('HH', `0${this.getHours()}`.slice(-2))
                .replace('H', this.getHours())
                .replace('mm', `0${this.getMinutes()}`.slice(-2))
                .replace('m', this.getMinutes())
                .replace('ss', `0${this.getSeconds()}`.slice(-2))
                .replace('s', this.getSeconds())
                .replace('SSS', `00${this.getMilliseconds()}`.slice(-3));
        };
        var MoveType = {
            None: 0,
            Bar: 1,
            DragBar: 2,
            Slider: 3,
            SliderBorder: 4,
        };
        var MouseType = {
            Left: 1,
            Middle: 2,
            Right: 3,
        };
        var global_left_ts_ = 0, global_right_ts_ = 0;
        var percent_left_ = 0.45, percent_right_ = 0.55;
        var on_mousemove_ = null, move_type_ = MoveType.None;

        function timeaxis_busyness(b) {
            if (b) {
                $('body').addClass('busyness');
                $('#timeaxis_bar').addClass('busyness');
                $('#timeaxis_slider').addClass('busyness');
                $('.timeaxis_slider_border').addClass('busyness');
                $('#loading_div').addClass('show');
            } else {
                $('body').removeClass('busyness');
                $('#timeaxis_bar').removeClass('busyness');
                $('#timeaxis_slider').removeClass('busyness');
                $('.timeaxis_slider_border').removeClass('busyness');
                $('#loading_div').removeClass('show');
            }
        }

        function timeaxis_grabbing(b) {
            if (b) {
                $('body').addClass('grabbing');
                $('#timeaxis_bar').addClass('grabbing');
                $('#timeaxis_slider').addClass('grabbing');
                $('.timeaxis_slider_border').addClass('grabbing');
            } else {
                $('body').removeClass('grabbing');
                $('#timeaxis_bar').removeClass('grabbing');
                $('#timeaxis_slider').removeClass('grabbing');
                $('.timeaxis_slider_border').removeClass('grabbing');
            }
        }

        var timerange_min_ = new Date(0);
        var is_realtime_ = false;
        var realtime_ = null;
        function set_timeaxis_realtime(b) {
            if (is_realtime_ == b) {
                return;
            }
            is_realtime_ = b;
            if (b) {
                $('#timeaxis_realtime').text('⏸️');
                if (realtime_ == null)
                    realtime_ = setInterval('on_realtime_update()', 1000);
                on_realtime_update();
                if (realtime_ == null)
                    realtime_ = setInterval('on_realtime_update()', 1000);
            } else {
                $('#timeaxis_realtime').text('▶️');
                if (realtime_ != null) {
                    clearInterval(realtime_);
                    realtime_ = null;
                }
            }
            realtime_change_cb_(b);
        }

        function on_realtime_update() {
            if (!is_realtime_) {
                _set_timeaxis_future();
                return;
            }
            let range_ts = 10 * 60 * 1000; // millis of 10 min
            let slider_ts = 60 * 1000; // millis of 1 min
            var now_ts = new Date().getTime() - time_diff_millis_;
            var slider_right = now_ts + slider_ts * 0.1;
            var slider_left = slider_right - slider_ts;
            var new_global_right_ts = slider_right;
            var new_global_left_ts = new_global_right_ts - range_ts;
            var percent_left = (slider_left - new_global_left_ts) / range_ts;
            var percent_right = (slider_right - new_global_left_ts) / range_ts;

            // apply
            set_timeaxis_range(new_global_left_ts, new_global_right_ts);
            set_timeaxis_percent(percent_left, percent_right, false);

            // update view
            if (layout_ != null) {
                layout_.xaxis.range = [slider_left, slider_right];
            }
        }

        function _get_current_percent(e) {
            var bar_offset_x = Math.round($('#timeaxis_bar').offset().left);
            var bar_width = Math.round($('#timeaxis_bar').width() - 2);
            return (e.pageX - bar_offset_x) / bar_width;
        }

        function update_timeaxis_tooltip(percent) {
            var bar_width = Math.round($('#timeaxis_bar').width() - 2);
            $('#timeaxis_tooltip').css('left', `${Math.round(bar_width * percent - 70)}px`);
            var ts = Math.round(percent * (global_right_ts_ - global_left_ts_) + global_left_ts_);
            $('#timeaxis_tooltip').text(new Date(ts).format('yyyy-MM-dd HH:mm:ss'));
        }
        function _adjust_range() {
            var realtime = false;
            if (percent_left_ >= 0.0001 && percent_right_ <= 0.9999) {
                set_timeaxis_realtime(realtime);
                return;
            }
            var left_ts = get_ts_from_percent(percent_left_);
            var right_ts = get_ts_from_percent(percent_right_);
            var new_global_left_ts = global_left_ts_;
            var new_global_right_ts = global_right_ts_;
            var step_ts = Math.round((global_right_ts_ - global_left_ts_) * 0.3);
            if (percent_right_ > 0.9999) {
                new_global_right_ts += step_ts;
                // var now_ts = new Date().getTime();
                // if (new_global_right_ts >= now_ts) {
                //     new_global_right_ts = now_ts;
                //     realtime = true;
                // }
            }
            if (percent_left_ < 0.0001) {
                new_global_left_ts -= step_ts;
            }

            // apply
            set_timeaxis_range(new_global_left_ts, new_global_right_ts);
            var percent_left = (left_ts - global_left_ts_) / (global_right_ts_ - global_left_ts_);
            var percent_right = (right_ts - global_left_ts_) / (global_right_ts_ - global_left_ts_);
            set_timeaxis_percent(percent_left, percent_right, false);

            set_timeaxis_realtime(realtime);
        }
        function force_view_range(left_ts, right_ts, update_view) {
            set_timeaxis_realtime(false);
            var new_global_left_ts = Math.min(global_left_ts_, left_ts);
            var new_global_right_ts = Math.max(global_right_ts_, right_ts);
            var range_ts = new_global_right_ts - new_global_left_ts;
            var percent_left = (left_ts - new_global_left_ts) / range_ts;
            var percent_right = (right_ts - new_global_left_ts) / range_ts;
            var percent_range = percent_right - percent_left;
            if (percent_range < 0.05 || percent_range > 0.8) {
                var mid_global_ts = (left_ts + right_ts) / 2;
                var global_size_ts = (right_ts - left_ts) / 0.8;
                if (percent_range < 0.05) {
                    global_size_ts = (right_ts - left_ts) / 0.05;
                }
                new_global_left_ts = mid_global_ts - global_size_ts / 2;
                new_global_right_ts = mid_global_ts + global_size_ts / 2;
                range_ts = new_global_right_ts - new_global_left_ts;
                percent_left = (left_ts - new_global_left_ts) / range_ts;
                percent_right = (right_ts - new_global_left_ts) / range_ts;
            } else if (percent_left < 0 || percent_right > 1) {
                if (percent_left < 0) {
                    new_global_left_ts = left_ts;
                    new_global_right_ts = new_global_left_ts + range_ts;
                } else {
                    new_global_right_ts = right_ts;
                    new_global_left_ts = new_global_right_ts - range_ts;
                }
                percent_left = (left_ts - new_global_left_ts) / range_ts;
                percent_right = (right_ts - new_global_left_ts) / range_ts;
            }
            set_timeaxis_range(new_global_left_ts, new_global_right_ts);
            set_timeaxis_percent(percent_left, percent_right, update_view);
        }
        function init_timeaxis(locale, timerange_min, global_left_ts, global_right_ts) {
            timerange_min_ = Math.round(timerange_min / 1000);
            $.datetimepicker.setLocale(locale);
            var minDate = new Date(timerange_min_).format("yyyy-MM-dd HH:mm:ss.SSS");
            var maxDate = new Date().format("yyyy-MM-dd");
            $('#datetimepicker').val(new Date(new Date() - time_diff_millis_).format("yyyy-MM-dd HH:mm:ss.SSS"));
            $('#datetimepicker').datetimepicker({
                format: 'Y-m-d H:i:s',
                minDate: minDate,
                maxDate: maxDate,
                theme: 'light', // dark
                //timepicker: false
            });
            $('#filtertime_btn').click(flutter_filter_time);
            $('#datetimepicker_btn').click(function () {
                var global_left_ts = 0, global_right_ts = 0;
                var percent_left = 0, percent_right = 0;
                let half_range = parseInt($('#datetimerange').val()) * 500;
                var time = $('#datetimepicker').val();
                if (time.length == 10) {
                    time = new Date(`${time} 12:00:00`).getTime();
                } else if (time.length == 13) {
                    time = new Date(`${time}:30:00`).getTime();
                } else if (time.length == 16) {
                    time = new Date(`${time}:30`).getTime();
                } else if (time.length == 19 || time.length == 23) {
                    time = new Date(time).getTime();
                } else {
                    time = new Date(new Date() - time_diff_millis_ - half_range * 0.9).getTime();
                }
                let tmp_left = time - half_range;
                let tmp_right = time + half_range;
                global_left_ts = tmp_left - (tmp_right - tmp_left) * 2;
                global_right_ts = tmp_right + (tmp_right - tmp_left) * 2;
                percent_left = 0.4;
                percent_right = 0.6;
                set_timeaxis_realtime(false);
                set_timeaxis_range(global_left_ts, global_right_ts);
                set_timeaxis_percent(percent_left, percent_right, true);
            });
            //
            set_timeaxis_range(global_left_ts, global_right_ts);
            set_timeaxis_percent(percent_left_, percent_right_, true);
            //
            $('#timeaxis').contextmenu(function (e) {
                e.preventDefault();
                set_timeaxis_realtime(false);
            });
            $('#timeaxis_bar').mousedown(function (e) {
                e.preventDefault();
                set_timeaxis_realtime(false);
                if (move_type_ != MoveType.None) {
                    return;
                }
                if (e.which == MouseType.Left) {
                    move_type_ = MoveType.Bar;
                    $('#timeaxis_hoverbar').addClass('show');
                    var start_percent = _get_current_percent(e);
                    var bar_width = Math.round($('#timeaxis_bar').width() - 2);
                    var left = Math.round(start_percent * bar_width);
                    $('#timeaxis_hoverbar').css('left', left);
                    $('#timeaxis_hoverbar').css('width', '2px');
                    $('#timeaxis_hoverbar').attr('start_percent', start_percent.toFixed(4));
                    on_mousemove_ = function (e) {
                        var percent = _get_current_percent(e);
                        update_timeaxis_tooltip(percent);
                        var left = Math.round(start_percent * bar_width);
                        var right = Math.round(percent * bar_width);
                        if (left > right) {
                            var tmp = left;
                            left = right;
                            right = tmp;
                        }
                        $('#timeaxis_hoverbar').css('left', left);
                        $('#timeaxis_hoverbar').css('width', `${right - left}px`);
                    };
                } else if (e.which == MouseType.Right) {
                    move_type_ = MoveType.DragBar;
                    timeaxis_grabbing(true);
                    var timerange_ts = global_right_ts_ - global_left_ts_;
                    var start_percent = _get_current_percent(e);
                    var start_ts = start_percent * timerange_ts + global_left_ts_;
                    on_mousemove_ = function (e) {
                        var percent = _get_current_percent(e);
                        update_timeaxis_tooltip(percent);
                        var new_global_left_ts = start_ts - percent * timerange_ts;
                        var new_global_right_ts = new_global_left_ts + timerange_ts;
                        var left_ts = get_ts_from_percent(percent_left_);
                        var right_ts = get_ts_from_percent(percent_right_);

                        // apply
                        var percent_left = (left_ts - new_global_left_ts) / (new_global_right_ts - new_global_left_ts);
                        var percent_right = (right_ts - new_global_left_ts) / (new_global_right_ts - new_global_left_ts);
                        set_timeaxis_range(new_global_left_ts, new_global_right_ts);
                        set_timeaxis_percent(percent_left, percent_right, false);
                    };
                }
            });
            $('#timeaxis_slider').mousedown(function (e) {
                e.preventDefault();
                set_timeaxis_realtime(false);
                if (move_type_ != MoveType.None) {
                    return;
                }
                move_type_ = MoveType.Slider;
                timeaxis_grabbing(true);
                //var right_offset_x = Math.round($('#timeaxis_slider_border_right').offset().left + 3);
                var bar_width = Math.round($('#timeaxis_bar').width() - 2);
                var mouse_border_left_percent = _get_current_percent(e) - percent_left_;
                var mouse_border_right_percent = percent_right_ - percent_left_ - mouse_border_left_percent;
                on_mousemove_ = function (e) {
                    var percent = _get_current_percent(e);
                    update_timeaxis_tooltip(percent);
                    var percent_left = percent - mouse_border_left_percent;
                    var percent_right = percent + mouse_border_right_percent;
                    // // range limit
                    //if (percent_left <= 0) {
                    //    percent_right -= percent_left;
                    //    percent_left = 0;
                    //} else if (percent_right > 1) {
                    //    percent_left -= (percent_right - 1);
                    //    percent_right = 1;
                    //}
                    set_timeaxis_percent(percent_left, percent_right, false);
                };
                return true;
            });
            $('.timeaxis_slider_border').mousedown(function (e) {
                e.preventDefault();
                set_timeaxis_realtime(false);
                if (move_type_ != MoveType.None) {
                    return;
                }
                move_type_ = MoveType.SliderBorder;
                timeaxis_grabbing(true);
                var percent = _get_current_percent(e);
                var cur_left = Math.abs(percent - percent_left_) <= Math.abs(percent - percent_right_);
                var percent_tmp = percent_left_;
                if (cur_left) {
                    percent_tmp = percent_right_;
                }
                on_mousemove_ = function (e) {
                    var percent = _get_current_percent(e);
                    // // range limit
                    //if (percent < 0) {
                    //    percent = 0;
                    //} else if (percent >= 1) {
                    //    percent = 1;
                    //}
                    update_timeaxis_tooltip(percent);
                    if (percent < percent_tmp) {
                        set_timeaxis_percent(percent, percent_tmp, false);
                    } else {
                        set_timeaxis_percent(percent_tmp, percent, false);
                    }
                };
                return true;
            });

            // Move both fingers in the same direction
            var wheel_data_valid_ = false;
            var wheel_delta_x_ = 0, wheel_delta_y_ = 0;
            var wheel_cache_global_left_ts_ = 0;
            var wheel_cache_global_right_ts_ = 0;
            var wheel_cache_left_ts_ = 0;
            var wheel_cache_right_ts_ = 0;
            var wheel_current_ts_ = 0;
            var wheel_mouse_ts_ = 0;
            $('#timeaxis').on('mousewheel', function (e) {
                e.preventDefault();
                set_timeaxis_realtime(false);
                if (!wheel_data_valid_) {
                    wheel_data_valid_ = true;
                    wheel_delta_x_ = wheel_delta_y_ = 0;
                    wheel_cache_global_left_ts_ = global_left_ts_;
                    wheel_cache_global_right_ts_ = global_right_ts_;
                    wheel_cache_left_ts_ = get_ts_from_percent(percent_left_);
                    wheel_cache_right_ts_ = get_ts_from_percent(percent_right_);
                    var start_percent = _get_current_percent(e);
                    wheel_mouse_ts_ = Math.round(start_percent * (global_right_ts_ - global_left_ts_) + global_left_ts_);
                }
                wheel_delta_x_ += e.deltaX;
                wheel_delta_y_ += e.deltaY;

                // calc new range
                var timerange_ts = wheel_cache_global_right_ts_ - wheel_cache_global_left_ts_;
                var new_mouse_ts = Math.round(wheel_mouse_ts_ + timerange_ts * (wheel_delta_x_ / 1000.0));
                var zoom_ratio = 1 / Math.pow(2, wheel_delta_y_ / 100.0);
                var new_global_left_ts = Math.round(new_mouse_ts - (wheel_mouse_ts_ - wheel_cache_global_left_ts_) * zoom_ratio);
                var new_global_right_ts = Math.round(new_mouse_ts + (wheel_cache_global_right_ts_ - wheel_mouse_ts_) * zoom_ratio);

                // apply
                timerange_ts = new_global_right_ts - new_global_left_ts;
                var percent_left = (wheel_cache_left_ts_ - new_global_left_ts) / timerange_ts;
                var percent_right = (wheel_cache_right_ts_ - new_global_left_ts) / timerange_ts;
                set_timeaxis_range(new_global_left_ts, new_global_right_ts);
                set_timeaxis_percent(percent_left, percent_right, false);
            });

            // $('#timeaxis').on('mousewheel DOMMouseScroll', function (e) {
            //     e.preventDefault();
            //     var delta = (e.originalEvent.wheelDelta || -e.originalEvent.detail) / 100.0;
            //     //
            //     var percent = _get_current_percent(e);
            //     var in_slider = percent >= percent_left_ && percent <= percent_right_;
            //     in_slider = false;
            //     if (in_slider) {
            //         var left_offset_x = Math.round($('#timeaxis_slider_border_left').offset().left + 5);
            //         var mouse_border_left_percent = (e.pageX - left_offset_x) / bar_width;
            //         var mouse_border_right_percent = percent_right_ - percent_left_ - mouse_border_left_percent;
            //         var percent_left = percent - mouse_border_left_percent * (100 - delta);
            //         var percent_right = percent + mouse_border_right_percent * (100 - delta);
            //         // // range limit
            //         // if (percent_left < 0) {
            //         //     percent_left = 0;
            //         // }
            //         // if (percent_right > 1) {
            //         //     percent_right = 1;
            //         // }
            //         set_timeaxis_percent(percent_left, percent_right, true);
            //     } else {
            //         // calc new range
            //         var timerange_ts = global_right_ts_ - global_left_ts_;
            //         var mouse_value_ts = Math.round(timerange_ts * percent + global_left_ts_);
            //         var range_left_ts = (mouse_value_ts - global_left_ts_);
            //         var range_right_ts = (global_right_ts_ - mouse_value_ts);
            //         if (delta > 0) {
            //             range_left_ts /= delta;
            //             range_right_ts /= delta;
            //         } else {
            //             range_left_ts *= -delta;
            //             range_right_ts *= -delta;
            //         }
            //         var cur_left_ts = Math.round(percent_left_ * timerange_ts + global_left_ts_);
            //         var cur_right_ts = Math.round(percent_right_ * timerange_ts + global_left_ts_);

            //         // check overlimit
            //         var new_global_left_ts = mouse_value_ts - range_left_ts;
            //         var new_global_right_ts = mouse_value_ts + range_right_ts;
            //         timerange_ts = new_global_right_ts - new_global_left_ts;
            //         var new_percent_left = (cur_left_ts - new_global_left_ts) / timerange_ts;
            //         var new_percent_right = (cur_right_ts - new_global_left_ts) / timerange_ts;
            //         // // range limit
            //         // if (new_percent_left < 0 || new_percent_right > 1) {
            //         //     if (percent_left_ == 0 || percent_right_ == 1) {
            //         //         return;
            //         //     }
            //         //     if (new_percent_left < 0) {
            //         //         new_global_left_ts = cur_left_ts;
            //         //     }
            //         //     if (new_percent_right > 1) {
            //         //         new_global_right_ts = cur_right_ts;
            //         //     }
            //         //     timerange_ts = new_global_right_ts - new_global_left_ts;
            //         //     new_percent_left = (cur_left_ts - new_global_left_ts) / timerange_ts;
            //         //     new_percent_right = (cur_right_ts - new_global_left_ts) / timerange_ts;
            //         // }

            //         // apply
            //         set_timeaxis_range(new_global_left_ts, new_global_right_ts);
            //         set_timeaxis_percent(new_percent_left, new_percent_right, false);
            //     }
            // });
            $(document).mousemove(function (e) {
                wheel_data_valid_ = false;
                if (on_mousemove_ != null) {
                    on_mousemove_(e);
                } else {
                    var percent = _get_current_percent(e);
                    update_timeaxis_tooltip(percent);
                }
            });
            $(document).mouseup(function (e) {
                wheel_data_valid_ = false;
                on_mousemove_ = null;
                timeaxis_grabbing(false);
                if (move_type_ == MoveType.Slider) {
                    _adjust_range();
                    set_timeaxis_percent(percent_left_, percent_right_, true);
                } else if (move_type_ == MoveType.SliderBorder) {
                    _adjust_range();
                    set_timeaxis_percent(percent_left_, percent_right_, true);
                } else if (move_type_ == MoveType.Bar) {
                    $('#timeaxis_hoverbar').removeClass('show');
                    var start_percent = parseFloat($('#timeaxis_hoverbar').attr('start_percent'));
                    var end_percent = _get_current_percent(e);
                    // if (end_percent < 0) {
                    //     end_percent = 0;
                    // } else if (end_percent > 1) {
                    //     end_percent = 1;
                    // }
                    if (start_percent > end_percent) {
                        var tmp = start_percent;
                        start_percent = end_percent;
                        end_percent = tmp;
                    }
                    set_timeaxis_percent(start_percent, end_percent, true);
                    setTimeout('_adjust_range()', 200);
                }
                move_type_ = MoveType.None;
            });
            $('#timeaxis_realtime').click(function () {
                set_timeaxis_realtime(!is_realtime_);
            });
            $('#timeaxis_reset').click(function () {
                var cur_time = new Date().getTime() - time_diff_millis_;
                if (cur_time <= global_left_ts_ || timerange_min_ >= global_right_ts_) {
                    set_timeaxis_realtime(true);
                    return;
                } else {
                    var left_ts = get_ts_from_percent(percent_left_);
                    var right_ts = get_ts_from_percent(percent_right_);
                    var global_left_ts = left_ts - Math.round((right_ts - left_ts) * 4.5);
                    var global_right_ts = right_ts + Math.round((right_ts - left_ts) * 4.5);
                    set_timeaxis_range(global_left_ts, global_right_ts);
                    set_timeaxis_percent(0.45, 0.55, false);
                }
                // var left_ts = get_ts_from_percent(percent_left_);
                // var right_ts = get_ts_from_percent(percent_right_);
                // var global_left_ts = (left_ts + right_ts) / 2 - 12 * 60 * 60 * 1000;
                // var global_right_ts = global_left_ts + 24 * 60 * 60 * 1000;
                // global_left_ts = Math.min(global_left_ts, left_ts);
                // global_right_ts = Math.max(global_right_ts, right_ts);
                // var percent_left = (left_ts - global_left_ts) / (global_right_ts - global_left_ts);
                // var percent_right = (right_ts - global_left_ts) / (global_right_ts - global_left_ts);

                // // apply
                // set_timeaxis_range(global_left_ts, global_right_ts);
                // set_timeaxis_percent(percent_left, percent_right, false);
            });
            $(window).resize(function () {
                wheel_data_valid_ = false;
                set_timeaxis_percent(percent_left_, percent_right_, false);
            });
        }

        var timeaxis_timerange_change_cb_ = null;
        function set_timeaxis_timerange_change(timeaxis_timerange_change_cb) {
            timeaxis_timerange_change_cb_ = timeaxis_timerange_change_cb;
            timeaxis_timerange_change_cb_(percent_left_, percent_right_);
        }

        var realtime_change_cb_ = null;
        function set_realtime_change(realtime_change_cb) {
            realtime_change_cb_ = realtime_change_cb;
        }

        var update_elapsed_ts_ = 0;
        var single_millis_ = null;
        function force_set_timeaxis(global_left_ts, left_ts, right_ts, global_right_ts) {
            $('#datetimepicker').val(new Date(Math.round((left_ts + right_ts) / 2)).format("yyyy-MM-dd HH:mm:ss.SSS"));
            var percent_left = (left_ts - global_left_ts) / (global_right_ts - global_left_ts);
            var percent_right = (right_ts - global_left_ts) / (global_right_ts - global_left_ts);
            if (global_left_ts_ != global_left_ts || percent_left_ != percent_left || global_right_ts_ != global_right_ts || percent_right_ != percent_right) {
                set_timeaxis_realtime(false);
                global_left_ts_ = global_left_ts;
                global_right_ts_ = global_right_ts;
                set_timeaxis_percent(percent_left, percent_right, true);
                _adjust_range();
            }
            timeaxis_update_single_millis();
        }
        function set_timeaxis_percent(percent_left, percent_right, update_view) {
            set_timeaxis_percent_impl(percent_left, percent_right);
            if (update_view && timeaxis_timerange_change_cb_ != null) {
                var cur_time = new Date().getTime() - time_diff_millis_;
                if (cur_time >= update_elapsed_ts_) {
                    update_elapsed_ts_ = cur_time + 1000;
                    timeaxis_timerange_change_cb_(percent_left_, percent_right_);
                }
            }
            timeaxis_update_single_millis();
        }

        function set_timeaxis_range(global_left_ts, global_right_ts) {
            global_left_ts_ = global_left_ts;
            global_right_ts_ = global_right_ts;
            $('#timeaxis_label_1').text(new Date(global_left_ts).format('yyyy-MM-dd HH:mm:ss'));
            $('#timeaxis_label_2').text(new Date(global_right_ts).format('yyyy-MM-dd HH:mm:ss'));
            _set_timeaxis_future();
        }

        function _set_timeaxis_future() {
            var now = new Date().getTime() - time_diff_millis_;
            if (now < global_right_ts_) {
                $('#timeaxis_future').addClass('show');
                var bar_width = Math.round($('#timeaxis_bar').width() - 2);
                var now_percent = (now - global_left_ts_) / (global_right_ts_ - global_left_ts_);
                var left = Math.round(now_percent * bar_width);
                $('#timeaxis_future').css('left', left);
            } else {
                $('#timeaxis_future').removeClass('show');
            }
        }

        function set_timeaxis_percent_impl(percent_left, percent_right) {
            percent_left_ = percent_left;
            percent_right_ = percent_right;
            var need_show_slider = percent_right > 0 && percent_left < 1;
            if (need_show_slider) {
                var cur_time = new Date().getTime() - time_diff_millis_;
                if (cur_time <= global_left_ts_ || timerange_min_ >= global_right_ts_)
                    need_show_slider = false;
            }
            if (need_show_slider) {
                if (percent_left < 0) percent_left = 0;
                if (percent_right > 1) percent_right = 1;
                $('#timeaxis_slider').addClass('show');
                $('#timeaxis_reset').removeClass('show');
            } else {
                $('#timeaxis_slider').removeClass('show');
                $('#timeaxis_reset').addClass('show');
            }
            var bar_width = Math.round($('#timeaxis_bar').width() - 2);
            $('#timeaxis_slider').css('margin-left', `${bar_width * percent_left}px`);
            $('#timeaxis_slider').css('width', `${bar_width * (percent_right - percent_left)}px`);
            var bar_offset_x = Math.round($('#timeaxis_bar').offset().left);
            $('#timeaxis_label_1').css('margin-left', `${bar_offset_x - 80}px`);
            $('#timeaxis_label_2').css('margin-left', `${bar_offset_x + bar_width - 80}px`);
        }

        function timeaxis_update_single_millis() {
            if (single_millis_ && single_millis_ >= global_left_ts_ && single_millis_ < global_right_ts_) {
                $('#timeaxis_single_mark').addClass('show');
                var bar_width = Math.round($('#timeaxis_bar').width() - 2);
                var percent_single = (single_millis_ - global_left_ts_) / (global_right_ts_ - global_left_ts_);
                $('#timeaxis_single_mark').css('margin-left', `${bar_width * percent_single}px`);
            } else {
                $('#timeaxis_single_mark').removeClass('show');
            }
        }

        function get_ts_from_percent(percent) {
            var timerange_ts = global_right_ts_ - global_left_ts_;
            return Math.round(percent * timerange_ts + global_left_ts_);
        }

        function get_percent_from_ts(ts) {
            var timerange_ts = global_right_ts_ - global_left_ts_;
            return (ts - global_left_ts_) / timerange_ts;
        }
    </script>
</head>

<body style="margin: 0px; display: flex; flex-direction: column; height: 100vh; overflow: hidden;">
    <div id="timeaxis_div" style="width: 100%; height: 54px; display: flex; user-select: none;">
        <div style="padding-left: 20px; padding-right: 20px; height: 20px; margin-top: 17px;">
            <input id="filtertime_btn" type="button" value="filter time" />
            <input id="datetimepicker" type="text" autocomplete="off" style="width: 160px;" />
            <select id="datetimerange" style="width: 80px;">
                <option value="60" selected="selected">1 min</option>
                <option value="600">10 min</option>
                <option value="3600">1 hour</option>
                <option value="36000">10 hour</option>
                <option value="86400">1 day</option>
                <option value="864000">10 day</option>
            </select>
            <input id="datetimepicker_btn" type="button" value="jump" />
        </div>
        <div id="timeaxis" style="flex-grow: 1; margin-right: 20px; position: relative;">
            <div id="timeaxis_bar">
                <div id="timeaxis_future">
                </div>
            </div>
            <div id="timeaxis_hoverbar">
            </div>
            <div id="timeaxis_single_mark">
            </div>
            <div id="timeaxis_slider" class="show">
                <div id="timeaxis_slider_border_left" class="timeaxis_slider_border" style="left: -7px;"></div>
                <div id="timeaxis_slider_border_right" class="timeaxis_slider_border" style="right: -7px;"></div>
            </div>
            <button id="timeaxis_reset" class="timeaxis_btn">↻</button>
            <span id="timeaxis_tooltip">aaa</span>
        </div>
        <div style="width: 50px;">
            <button id="timeaxis_realtime" class="timeaxis_btn">▶️</button>
        </div>
    </div>
    <div id="timeaxis_label" style="height: 24px; width: 100%; position: relative; user-select: none;">
        <span id="timeaxis_label_1">2024-01-01 13:03:21</span>
        <span id="timeaxis_label_2">2024-01-01 13:03:21</span>
    </div>
    <div id="extendtext_parent">
        <p id="extendtext">&emsp;</p>
    </div>
    <!-- Plotly chart will be drawn inside this DIV -->
    <div id="graph_parent_div" style="flex-grow: 1; position: relative;">
        <div id="graph_div" style="height: 100%;"></div>
        <div id="graph_mark1" class="line hide" style="position: absolute; left: 100px; top: 100px; width: 200px;">
        </div>
    </div>
    <div id="loading_div" style="position: absolute; width: 100%; margin-top: 300px; display: none;">
        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid" width="200"
            height="200"
            style="shape-rendering: auto; display: block; animation: spin 1s linear infinite; margin: 0px auto;">
            <g data-idx="1">
                <path d="M10 50A40 40 0 0 0 90 50A40 42 0 0 1 10 50" fill="#faca30" stroke="none" data-idx="2"
                    transform="matrix(-0.37,-0.93,0.93,-0.37,20.99,116.26)">
                </path>
            </g>
        </svg>
    </div>
    <div id="next_view"
        style="position: absolute; left: 0px; top: 0px; right: 0px; bottom: 0px; background-color: #7777; z-index: 1000;">
        <iframe id="next_view_frame" src="about:blank"
            style="position: absolute; left: 4%; top: 5%; width: 92%; height: 90%;"></iframe>
        <span id="next_view_close"
            style="display: block; position: absolute; width: 20px; height: 20px; right: 25px; top: 40px; font-size: 20px; cursor: pointer;">✖</span>
    </div>
    <div id="next_view_menu" style="position: absolute; left: 0px; top: 0px; right: 0px; bottom: 0px; z-index: 1001;">
        <div
            style="position: absolute; width: 200px; background-color: #EEE; border: 1px solid #333; border-radius: 5px; left: 50%; margin-left: -100px; top: 200px;">
            <div>
                <h3 id="menu_choice"></h3>
            </div>
            <div><span id="menu_agvLog" data-page="agvLog"></span></div>
            <div><span id="menu_radarRecord" data-page="radarRecord"></span></div>
            <div><span id="menu_sensorSwitch" data-page="sensorSwitch"></span></div>
            <div><span id="menu_battery" data-page="battery"></span></div>
            <div><span id="menu_motionCurve" data-page="motionCurve"></span></div>
            <div><span id="menu_loadRatio" data-page="loadRatio"></span></div>
            <div><span id="menu_distanceSensor" data-page="distanceSensor"></span></div>
        </div>
    </div>
    <script type="application/javascript">
        var call_flutter_ = [];
        function invoke_flutter(func, args) {
            call_flutter_.push({ func: func, args: args });
        }

        var view_left_ts_ = 0, view_right_ts_ = 0;
        function flutter_update_view_range(left_ts, right_ts, bottom_val, top_val, ctrl_in_view, is_realtime) {
            var skip_redraw = false;
            if (ctrl_in_view && left_ts >= view_left_ts_ && right_ts <= view_right_ts_) {
                // zoom graph from trend data to detail data
                if (!is_detail_ && right_ts - left_ts < trend_range_limit_ts_) {
                    skip_redraw = true;
                }
            }
            if (!skip_redraw && layout_ != null) {
                layout_.xaxis.range = [left_ts, right_ts];
                //Plotly.restyle(graphDiv, layout_);
                _drawChart(data_, layout_, config_, update_, false, is_detail_);
            }
            view_left_ts_ = left_ts;
            view_right_ts_ = right_ts;
            invoke_flutter('update_view_range', [global_left_ts_, left_ts, right_ts, global_right_ts_, bottom_val, top_val, is_realtime]);
            timeaxis_busyness(true);
        }

        function flutter_set_realtime(realtime_play) {
            invoke_flutter('set_realtime', [realtime_play]);
        }

        function flutter_set_dragmode(dragmode) {
            invoke_flutter('set_dragmode', [dragmode]);
        }

        function flutter_filter_time() {
            invoke_flutter('filter_time', []);
        }

        var all_lines_ = [], show_lines_ = [];
        function init_show_lines(show_lines) {
            if (all_lines_.length == 0) {
                all_lines_ = show_lines;
                show_lines_ = all_lines_.slice();
            }
        }
        function flutter_show_lines(lines) {
            show_lines_ = lines;
            invoke_flutter('show_lines', [lines]);
        }

        function set_extend_text(text) {
            if (text != '') {
                $('#extendtext').text(text);
                $('#extendtext').addClass('show');
            } else {
                $('#extendtext').text(' ');
                $('#extendtext').removeClass('show');
            }
        }

        var trend_range_limit_ts_ = 0;
        function enable_trend(trend_range_limit_ts) {
            trend_range_limit_ts_ = trend_range_limit_ts;
        }

        function init(locale, first_ts, bar_start, view_start, view_end, bar_end, is_realtime) {
            init_timeaxis(locale, first_ts, bar_start, bar_end);
            var percent_view_start = (view_start - bar_start) / (bar_end - bar_start);
            var percent_view_end = (view_end - bar_start) / (bar_end - bar_start);
            set_timeaxis_percent(percent_view_start, percent_view_end, true);
            set_timeaxis_timerange_change(function (left_percent, right_percent) {
                var left_ts = get_ts_from_percent(left_percent);
                var right_ts = get_ts_from_percent(right_percent);
                flutter_update_view_range(left_ts, right_ts, null, null, false, is_realtime_);
            });
            set_realtime_change(flutter_set_realtime);
            set_timeaxis_realtime(is_realtime);
        }

        var time_diff_millis_ = 0;
        function set_time_diff_millis(time_diff_millis) {
            time_diff_millis_ = time_diff_millis;
        }

        function _plotly_relayout(e) {
            if (e.hasOwnProperty('dragmode')) {
                flutter_set_dragmode(e['dragmode']);
            }
            if (e.hasOwnProperty('xaxis.range[0]') || e.hasOwnProperty('xaxis.range')) {
                var left_ts = 0, right_ts = 0;
                var view_bottom = null, view_top = null;
                if (e.hasOwnProperty('xaxis.range')) {
                    // range slider
                    left_ts = Date.parse(e['xaxis.range'][0]);
                    right_ts = Date.parse(e['xaxis.range'][1]);
                    view_bottom = e['yaxis.range'][0];
                    view_top = e['yaxis.range'][1];
                } else {
                    // zoom in the plot
                    left_ts = Date.parse(e['xaxis.range[0]']);
                    right_ts = Date.parse(e['xaxis.range[1]']);
                    view_bottom = e['yaxis.range[0]'];
                    view_top = e['yaxis.range[1]'];
                }
                var diff = right_ts - left_ts;
                var graphDiv = document.getElementById('graph_div');
                if (diff > 5000 || !is_detail_) {
                    Plotly.restyle(graphDiv, { mode: 'lines' });
                } else {
                    Plotly.restyle(graphDiv, { mode: 'lines+markers' });
                }

                force_view_range(left_ts, right_ts, false);
                flutter_update_view_range(left_ts, right_ts, view_bottom, view_top, true, is_realtime_);
            }
        }

        var in_wait_ = false;
        var last_visible_ts_ = 0;
        var last_visible_line_id_ = -1;
        function _update_visible(e) {
            var line_id = parseInt(e.data[e.curveNumber].userdata_id);
            //
            if (show_lines_.includes(line_id)) {
                show_lines_ = show_lines_.filter((item) => item != line_id);
            } else {
                show_lines_.push(line_id);
            }
            //
            var now_ts = new Date().getTime() - time_diff_millis_;
            if (now_ts - last_visible_ts_ < 200 && line_id == last_visible_line_id_) {
                if (show_lines_.includes(line_id) && show_lines_.length != 1) {
                    show_lines_ = [line_id];
                } else {
                    show_lines_ = all_lines_;
                }
                last_visible_ts_ = 0;
                last_visible_line_id_ = -1;
            } else {
                last_visible_ts_ = now_ts;
                last_visible_line_id_ = line_id;
            }
            if (in_wait_) return;
            in_wait_ = true;
            setTimeout(function () {
                in_wait_ = false;
                flutter_show_lines(show_lines_);
                for (var i = 0; i < data_.length; i += 1) {
                    if (show_lines_.includes(data_[i].userdata_id)) {
                        data_[i].visible = true;
                    } else {
                        data_[i].visible = "legendonly";
                    }
                }
                _drawChart(data_, layout_, config_, update_, false, is_detail_);
            }, 200);
        }

        var data_ = null;
        var layout_ = null;
        var config_ = null;
        var update_ = null;
        var is_detail_ = true;
        function drawChart(data, layout, config, update, auto_range, is_detail) {
            if (auto_range && layout_ != null) {
                layout.xaxis.range = layout_.xaxis.range;
            }
            config.modeBarButtonsToAdd.push({
                name: 'select_range',
                title: 'select range',
                icon: Plotly.Icons.drawline,
                click: function (gd) {
                    if (select_time_step_ == -1) {
                        set_timeaxis_realtime(false);
                        select_time_step_ = 0;
                        $('#graph_mark1').css('width', '200px');
                        $('#graph_mark1').removeClass('hide');
                        $('#graph_mark1').removeClass('line');
                        $('#graph_mark1').addClass('cross');
                        cache_time0_ = cache_time1_ = '';
                    } else {
                        select_time_step_ = -1;
                        $('#graph_mark1').addClass('hide');
                    }
                },
            });
            _drawChart(data, layout, config, update, auto_range, is_detail);
            $('#datetimepicker').val(new Date(Math.round((layout.xaxis.range[0] + layout.xaxis.range[1]) / 2)).format("yyyy-MM-dd HH:mm:ss.SSS"));
        }
        var cache_time0_ = '', cache_time1_ = '';
        var cache_time_last_process_millis_ = 0;
        var select_time_step_ = -1;
        var cache_time0_offset_x_ = 0;
        function _drawChart(data, layout, config, update, auto_range, is_detail) {
            data_ = data;
            layout_ = layout;
            config_ = config;
            update_ = update;
            is_detail_ = is_detail;
            timeaxis_busyness(false);

            var graphDiv = document.getElementById('graph_div');
            Plotly.newPlot(graphDiv, data, layout, config);
            Plotly.restyle(graphDiv, update);
            graphDiv.on('plotly_relayout', _plotly_relayout);
            graphDiv.on('plotly_legendclick', function (e) {
                _update_visible(e);
                return false;
            });
            graphDiv.on('plotly_legenddoubleclick', function (e) {
                return false;
            });
            graphDiv.on('plotly_hover', function (e) {
                var point = e.points[0];
                if (select_time_step_ == 0) {
                    cache_time0_ = point.x;
                    cache_time0_offset_x_ = e.event.pointerX;
                    $('#graph_mark1').css('left', e.event.pointerX);
                } else if (select_time_step_ == 1) {
                    cache_time1_ = point.x;
                    if (e.event.pointerX >= cache_time0_offset_x_) {
                        $('#graph_mark1').css('left', `${cache_time0_offset_x_}px`);
                        $('#graph_mark1').css('width', `${e.event.pointerX - cache_time0_offset_x_}px`);
                    } else {
                        $('#graph_mark1').css('left', `${e.event.pointerX}px`);
                        $('#graph_mark1').css('width', `${cache_time0_offset_x_ - e.event.pointerX}px`);
                    }
                }
            });
            $('#graph_div').click(function (event) {
                if (select_time_step_ == -1) return;
                if (select_time_step_ == 0 && cache_time0_ == '') return;
                if (select_time_step_ == 1 && cache_time1_ == '') return;
                var now_millis = new Date().getTime();
                if (now_millis - cache_time_last_process_millis_ < 500) return;
                cache_time_last_process_millis_ = now_millis;
                if (select_time_step_ == 0) {
                    $('#graph_mark1').addClass('line');
                    $('#graph_mark1').removeClass('cross');
                    $('#graph_mark1').css('width', '20px');
                    select_time_step_ = 1;
                } else if (select_time_step_ == 1) {
                    select_time_step_ = -1;
                    time0_ = Date.parse(cache_time0_);
                    time1_ = Date.parse(cache_time1_);
                    if (time0_ > time1_) {
                        var tmp = time0_;
                        time0_ = time1_;
                        time1_ = tmp;
                    }
                    cache_time0_ = '';
                    cache_time1_ = '';
                    $('#next_view_menu').show();
                }
            });
        }

        var time0_ = '', time1_ = '';
        var i18n_data_ = {};
        function set_i18n_data(data) {
            i18n_data_ = JSON.parse(data);
            $('#menu_choice').text(`${i18n_data_["pleaseChoice"]}: `);
            $('#menu_agvLog').text(`${i18n_data_["viewPage"]}: ${i18n_data_["agvLog"]}`);
            $('#menu_radarRecord').text(`${i18n_data_["viewPage"]}: ${i18n_data_["lidarReplay"]}`);
            $('#menu_sensorSwitch').text(`${i18n_data_["viewPage"]}: ${i18n_data_["sensor"]}`);
            $('#menu_battery').text(`${i18n_data_["viewPage"]}: ${i18n_data_["batteryData"]}`);
            $('#menu_motionCurve').text(`${i18n_data_["viewPage"]}: ${i18n_data_["motionCurve"]}`);
            $('#menu_loadRatio').text(`${i18n_data_["viewPage"]}: ${i18n_data_["loadRatio"]}`);
            $('#menu_distanceSensor').text(`${i18n_data_["viewPage"]}: ${i18n_data_["distanceSensor"]}`);
            $('#filtertime_btn').val(i18n_data_["filterTime"]);
            $('#datetimepicker_btn').val(i18n_data_["jump"]);
        }
        $(function () {
            $('#next_view').click(function () {
                $('#next_view_frame').attr('src', 'about:blank');
                $('#next_view').hide();
            });
            $('#next_view').hide();
            //
            $('#next_view_menu').hide();
            $('#next_view_menu span').click(function () {
                $('#next_view_menu').hide();
                $('#graph_mark1').addClass('hide');
                var page = $(this).attr('data-page');
                var range_start_ts = time0_ * 1000;
                var range_end_ts = time1_ * 1000;
                var dist_ts = range_end_ts - range_start_ts;
                var global_start_ts = range_start_ts - dist_ts;
                var global_end_ts = range_end_ts + dist_ts;
                var range = `globalStart=${global_start_ts}&globalEnd=${global_end_ts}&timeRangeStart=${range_start_ts}&timeRangeEnd=${range_end_ts}&timePoint=${(time0_ + time1_) * 500}`;
                let url = `${window.location.origin}/geye/?appPage=${page}&viewRange=${encodeURIComponent(range)}&showMenu=false`;
                $('#next_view_frame').attr('src', url);
                $('#next_view').show();
            });
            $('#next_view_menu').click(function () {
                $('#next_view_menu').hide();
                $('#graph_mark1').addClass('hide');
            });
        });
    </script>
</body>

</html>